﻿#region Usings

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading; 

#endregion

namespace Gonte.Networking
{
    /// <summary>
    /// Basic Tcp Server implementation
    /// </summary>
    public class TcpServer
        : INetworkConnectionHandler
    {
        #region Constants

        const int BufferSize = 4096;

        #endregion

        #region Methods

        public void Start(int port)
        {
            _listener = new TcpListener(IPAddress.Any, port);
            _listener.Start();

            BeginAccept();
        }

        public void Stop()
        {
            _listener.Stop();
        }

        public void Broadcast(string message)
        {
            foreach (NetworkStreamReaderWriter readerWriter in _connectedClients.Values)
            {
                readerWriter.Write(message);
            }
        }

        #endregion

        #region Helpers

        private void BeginAccept()
        {
            // Accept the connection. 
            _listener.BeginAcceptTcpClient(new AsyncCallback(EndAccept), null);

        }

        private void EndAccept(IAsyncResult ar)
        {
            // Accept the connection
            TcpClient tcpClient = _listener.EndAcceptTcpClient(ar);

            // Create and populate the client data
            ClientData clientData = CreateClientData();
            clientData.TcpClient = tcpClient;
            clientData.IpAddress = tcpClient.Client.RemoteEndPoint.ToString();

            // Create the stream reader/writer
            NetworkStreamReaderWriter readerWriter = new NetworkStreamReaderWriter(this, tcpClient.GetStream(), BufferSize, clientData);

            // Add the reader/writer to the connected clients so we can broadcast
            _connectedClients.Add(clientData.IpAddress, readerWriter);

            // Start reading the requests
            readerWriter.ReadAsynchronous();

            BeginAccept();
        } 

        #endregion

        #region Overridables

        public virtual ClientData CreateClientData()
        {
            return new ClientData();
        }

        public virtual void HandleRequest(NetworkStreamReaderWriter readerWriter, string request, bool hasMoreData)
        {
            throw new NotImplementedException();
        }

        public virtual void HandleClientDisconnected(NetworkStreamReaderWriter readerWriter)
        {
            // Remove the connected client
            _connectedClients.Remove(readerWriter.ClientData.IpAddress);
        }

        public virtual void HandleException(NetworkStreamReaderWriter readerWriter, Exception exception, bool isReading)
        {
            Exception innerException = ExceptionHelper.GetInnermostException(exception);
            readerWriter.Write(innerException.Message);
        }

        #endregion

        #region Fields

        private TcpListener _listener;
        private Dictionary<string, NetworkStreamReaderWriter> _connectedClients = new Dictionary<string, NetworkStreamReaderWriter>();

        #endregion
    }
}
